const { MIME_TYPE_EPUB, UPLOAD_URL, UPLOAD_PATH, OLD_UPLOAD_URL } = require('../utils/constant')
const fs = require('fs')
const path = require('path')
const Epub = require('../utils/epub')
const xml2js = require('xml2js').parseString
const { pid } = require('process')

class Book {
  constructor(file, data) {
    // file 刚上传的电子书文件--解析电子书数据
    // data 更新或者插入电子书数据
    if(file) {
      this.createBookFromFile(file)
    } else {
      this.createBookFromData(data)
    }
  }

  createBookFromFile(file) {
    const {
      destination,
      filename,
      path,
      originalname,
      mimetype = MIME_TYPE_EPUB
    } = file
    const suffix = mimetype === MIME_TYPE_EPUB ? '.epub' : ''
    // 源文件路径
    const oldBookPath = path
    // 加上文件后缀
    const bookPath = `${destination}/${filename}${suffix}`
    // 下载路径
    const url = `${UPLOAD_URL}/book/${filename}${suffix}`
    // 解压以后的文件路径
    const unzipPath = `${UPLOAD_PATH}/unzip/${filename}`
    // 解压后的文件夹URL--电子书阅读会使用到
    const unzipUrl = `${UPLOAD_URL}/unzip/${filename}`
    // 表示同步的判断
    if (!fs.existsSync(unzipPath)) {
      // 如果不存在 unzipPath 咱们就去创建一个 unzipPath
      fs.mkdirSync(unzipPath, { recursive: true })
    }
    if (fs.existsSync(oldBookPath) && !fs.existsSync(bookPath)) {
      // 文件重命名
      fs.renameSync(oldBookPath, bookPath)
    }
    this.fileName = filename  // 文件命
    this.path = `/book/${filename}${suffix}`  // 文件相对路径
    this.filePath = this.path
    this.unzipPath = `/unzip/${filename}`     // epub解压后的相对路径
    this.url = url   // 下载链接
    this.title = ''  // 电子书书名
    this.author = '' // 作者
    this.publisher = ''  // 出版社
    this.contents = []  // 目录
    this.cover = ''  // 封面图片URL
    this.coverPath = ''  // 封面图片路径
    this.category = -1  // 分类ID
    this.categoryText = ''  // 分类名称
    this.language = ''  // 语种
    this.unzipUrl = unzipUrl  // 解压后文件夹的链接
    this.originalname = originalname  // 电子书原名
  }

  createBookFromData(data) {
    this.fileName = data.fileName
    this.cover = data.coverPath
    this.title = data.title
    this.author = data.author
    this.publisher = data.publisher
    this.bookId = data.fileName
    this.language = data.language
    this.rootFile = data.rootFile
    this.originalname = data.originalname
    this.path = data.path || data.filePath
    this.filePath = data.path || data.filePath
    this.unzipPath = data.unzipPath
    this.coverPath = data.coverPath
    this.createUser = data.username
    this.createDt = new Date().getTime()
    this.updateDt = new Date().getTime()
    this.updateType = data.updateType === 0 ? data.updateType : 1
    this.category = data.category || 99
    this.categoryText = data.categoryText || '自定义'
    this.contents = data.contents || []
  }

  parse() {
    return new Promise((resolve, reject) => {
      const bookPath = `${UPLOAD_PATH}${this.filePath}`
      if (!fs.existsSync(bookPath)) {
        // 检查路径是否存在
        reject(new Error('电子书不存在'))
      }
      const epub = new Epub(bookPath)
      // 监听 err
      epub.on('error', err => {
        reject(err)
      })
      epub.on('end', err => {
        if (err) {
          reject(err)
        } else {
          const {
            language,
            creator,
            creatorFileAs,
            cover,
            title,
            publisher
          } = epub.metadata
          if (!title) {
            reject(new Error('图书标题为空'))
          } else {
            this.title = title
            this.language = language
            this.author = creator || creatorFileAs || 'unknown'
            this.publisher = publisher || 'unknown'
            this.rootFile = epub.rootFile
            // 获取封面
            // 解析到图片后的回调方法
            const handleGetImage = (err, data, mimeType) => {
              if (err) {
                reject(err)
              } else {
                const suffix = mimeType.split('/')[1]
                // 图片路径
                const coverPath = `${UPLOAD_PATH}/image/${this.fileName}.${suffix}`
                // 图片下载路径
                const coverUrl = `${UPLOAD_URL}/image/${this.fileName}.${suffix}`
                // Buffer 写入到磁盘当中 -- 原始 data 就是 buffer
                fs.writeFileSync(coverPath, data, 'binary')
                // 保存相对路径
                this.coverPath = `/image/${this.fileName}.${suffix}`
                this.cover = coverUrl
                resolve(this)
              }
            }
            // 解压电子书
            try {
              this.unzip()
              this.parseContents(epub).then(({chapters, chapterTree}) => {
                this.contents = chapters
                this.contentsTree = chapterTree
                epub.getImage(cover, handleGetImage)
              })
            } catch (e){
              reject(e)
            }
          }
        }
      })
      epub.parse()
    })
  }
  // 解压 epub 电子书
  unzip() {
    const AdmZip = require('adm-zip')
    const zip = new AdmZip(Book.genPath(this.path))
    zip.extractAllTo(Book.genPath(this.unzipPath), true)
  }

  parseContents(epub) {
    function getNcxFilePath() {
      const spine = epub && epub.spine
      const manifest = epub && epub.manifest
      const ncx = spine.toc && spine.toc.href
      const id = spine.toc && spine.toc.id
      if (ncx) {
        return ncx
      } else {
        return manifest[id].href
      }
    }

    function findParent(array, level = 0, pid= '')  {
      return array.map(item => {
        item.level = level
        item.pid = pid
        // 有子目录
        if (item.navPoint && item.navPoint.length > 0) {
          item.navPoint = findParent(item.navPoint, level + 1, item['$'].id)
        } else if (item.navPoint) {
          item.navPoint.level = level + 1
          item.navPoint.pid = item['$'].id
        }
        return item
      })
    }

    // 一维数组
    function flatten(array) {
      // 拷贝
      return [].concat(...array.map(item => {
        if (item.navPoint && item.navPoint.length > 0) {
          return [].concat(item, ...flatten(item.navPoint))
        } else if (item.navPoint) {
          return [].concat(item, item.navPoint)
        }
        return item
      }))
    }

    // 目录 路径
    const ncxFilePath = Book.genPath(`${this.unzipPath}/${getNcxFilePath()}`)
    if(fs.existsSync(ncxFilePath)) {
      // 读取和解析文件
      return new Promise((resolve, reject) => {
        const xml = fs.readFileSync(ncxFilePath, 'utf-8')
        const dir = path.dirname(ncxFilePath).replace(UPLOAD_PATH, '')
        const fileName = this.fileName
        const unzipPath = this.unzipPath
        console.log(dir, 'dir')
        xml2js(xml, {
          explicitArray: false,  // 不让解析成数组
          ignoreAttrs: false
        }, function(err, json) {
          if (err) {
            reject(err)
          } else {
            // 目标解析文件 navMap
            const navMap = json.ncx.navMap
            if (navMap.navPoint && navMap.navPoint.length > 0) {  // 目录对象校验
              navMap.navPoint = findParent(navMap.navPoint)
              const newNavMap = flatten(navMap.navPoint)
              const chapters = []
              newNavMap.forEach((chapter, index) => {
                // 章节 URL 路径
                const src = chapter.content['$'].src
                chapter.id = `${src}`
                chapter.href = `${dir}/${src}`.replace(unzipPath, '')
                chapter.text = `${UPLOAD_URL}${dir}/${src}`
                chapter.label = chapter.navLabel.text || ''
                chapter.navId = chapter['$'].id
                chapter.fileName = fileName
                chapter.order = index + 1
                chapters.push(chapter)
              })
              // 将一维数组转为树结构
              const chapterTree = Book.genContentsTree(chapters)
              resolve({chapters, chapterTree})
            } else {
              reject(new Error('目录解析失败，目录数为0'))
            }
          }
        })
      })
    } else {
      throw new Error('目录文件不存在')
    }
  }

  // 将数据库相关字段提取出来
  toDb() {
    return {
      fileName: this.fileName,
      cover: this.coverPath,
      title: this.title,
      author: this.author,
      publisher: this.publisher,
      bookId: this.fileName,
      language: this.language,
      rootFile: this.rootFile,
      originalname: this.originalname,
      filePath: this.filePath,
      unzipPath: this.unzipPath,
      coverPath: this.coverPath,
      createUser: this.createUser,
      createDt: this.createDt,
      updateDt: this.updateDt,
      updateType: this.updateType,
      category: this.category,
      categoryText: this.categoryText
    }
  }

  getContents() {
    return this.contents
  }

  reset() {
    if (Book.pathExists(this.filePath)) {
      fs.unlinkSync(Book.genPath(this.filePath))
    }
    if (Book.pathExists(this.coverPath)) {
      fs.unlinkSync(Book.genPath(this.coverPath))
    }
    if (Book.pathExists(this.unzipPath)) {
      // 迭代删除--低版本node不支持recursive
      fs.rmdirSync(Book.genPath(this.unzipPath), { recursive: true })
    }
  }

  static genPath(path) {
    // 判断路径前面是否事宜斜杠开头
    if (!path.startsWith('/')) {
      path = `/${path}`
    }
    return `${UPLOAD_PATH}${path}`
  }

  // 判断路径是否存在
  static pathExists(path) {
    if (path.startsWith(UPLOAD_PATH)) {
      return fs.existsSync(path)
    } else {
      return fs.existsSync(Book.genPath(path))
    }
  }

  static genCoverUrl(book) {
    // 老电子书
    const { cover } = book
    if (book.updateType === 0) {
      if (cover) {
        if (cover.startsWith('/')) {
          return `${OLD_UPLOAD_URL}${cover}`
        } else {
          return `${OLD_UPLOAD_URL}/${cover}`
        }
      } else {
        // 没有图片
        return null
      }
    } else {
      if (cover) {
        if (cover.startsWith('/')) {
          return `${UPLOAD_URL}${cover}`
        } else {
          return `${UPLOAD_URL}/${cover}`
        }
      } else {
        return null
      }
    }
  }

  static genContentsTree(contents) {
    if (contents) {
      const contentsTree = []
      contents.forEach(c => {
        c.children = []
        if (c.pid === '') {
          // 一级目录
          contentsTree.push(c)
        }else {
          // 有上级目录，找到对应的上级目录
          const parent = contents.find(_ => _.navId === c.pid)
          parent.children.push(c)
        }
      })
      return contentsTree
    }
  }
}

module.exports = Book
